package com.google.code.securityflow.ast

import com.google.code.securityflow.sabledd.node._
import java.io.{FileReader, PushbackReader, File}
import com.google.code.securityflow.sabledd.lexer.Lexer

class Parser(file: File) {

  val fileName = file.getName;

  object Counter {
    var count = 0;

    def next(): Int = {
      count = count + 1;
      count;
    }
  }

  def parse(): Program = {
    val reader = new PushbackReader(new FileReader(file), 100);
    val parser = new com.google.code.securityflow.sabledd.parser.Parser(new Lexer(reader));
    val start = parser.parse;

    new Program(Block(parseStmList(start.getGoal.getStmList)))
  }

  def parseExp(exp: PExp): Expression = {
    parseEqualityExp(exp.getEqualityExp);
  }

  def parseEqualityExp(exp: PEqualityExp): Expression = {
    exp match {
      case additiveExp: AAdditiveEqualityExp => {
        parseAdditiveExp(additiveExp.getAdditiveExp);
      }
      case eqExp: AEqualEqualityExp => {
        val location = SourceLocation(fileName, eqExp.getEq.getLine, eqExp.getEq.getPos);
        BinaryExpression(Counter.next(), EqualOp, parseEqualityExp(eqExp.getLeftExp), parseAdditiveExp(eqExp.getRightExp), location);
      }
      case neExp: ANotEqualEqualityExp => {
        val location = SourceLocation(fileName, neExp.getNe.getLine, neExp.getNe.getPos);
        BinaryExpression(Counter.next(), NotEqualOp, parseEqualityExp(neExp.getLeftExp), parseAdditiveExp(neExp.getRightExp), location);
      }
    }
  }

  def parseAdditiveExp(exp: PAdditiveExp): Expression = {
    exp match {
      case primaryExp: APrimaryAdditiveExp => {
        parsePrimaryExp(primaryExp.getPrimaryExp)
      }
      case plusExp: APlusAdditiveExp => {
        val location = SourceLocation(fileName, plusExp.getPlus.getLine, plusExp.getPlus.getPos);
        BinaryExpression(Counter.next(), PlusOp, parseAdditiveExp(plusExp.getLeftExp), parsePrimaryExp(plusExp.getRightExp), location);
      }
      case minusExp: AMinusAdditiveExp => {
        val location = SourceLocation(fileName, minusExp.getMinus.getLine, minusExp.getMinus.getPos);
        BinaryExpression(Counter.next(), MinusOp, parseAdditiveExp(minusExp.getLeftExp), parsePrimaryExp(minusExp.getRightExp), location);
      }
    }
  }

  def parsePrimaryExp(exp: PPrimaryExp): Expression = {
    exp match {
      case intLiteral: AIntegerLiteralPrimaryExp => {
        val location = SourceLocation(fileName, intLiteral.getInteger.getLine, intLiteral.getInteger.getPos);
        IntegerLiteralExpression(Counter.next(), intLiteral.getInteger.getText.toInt, false, location);
      }
      case intHighLiteral: AHighIntegerLiteralPrimaryExp => {
        val location = SourceLocation(fileName, intHighLiteral.getInteger.getLine, intHighLiteral.getInteger.getPos);
        IntegerLiteralExpression(Counter.next(), intHighLiteral.getInteger.getText.toInt, true, location);
      }
      case objectLiteral: AObjectLiteralPrimaryExp => {
        val location = SourceLocation(fileName, objectLiteral.getLBrace.getLine, objectLiteral.getLBrace.getPos);
        ObjectLiteralExpression(Counter.next(), location);
      }
      case functionLiteral: AFunctionLiteralPrimaryExp => {
        val location = SourceLocation(fileName, functionLiteral.getFunction.getLine, functionLiteral.getFunction.getPos);
        FunctionLiteralExpression(Counter.next(), Block(parseStmList(functionLiteral.getBody)), location);
      }
      case propertyAccess: APropertyAccessPrimaryExp => {
        parsePropertyAccess(propertyAccess.getPropertyAccess);
      }
    }
  }

  def parsePropertyAccess(pAcc: PPropertyAccess): PropertyAccessExpression = {
    pAcc match {
      case simple: ASimpleNamePropertyAccess => {
        val location = SourceLocation(fileName, simple.getName.getLine, simple.getName.getPos);
        SimplePropertyAccessExpression(Counter.next(), simple.getName.getText, location);
      }
      case qualified: AQualifiedNamePropertyAccess => {
        val location = SourceLocation(fileName, qualified.getName.getLine, qualified.getName.getPos);
        QualifiedPropertyAccessExpression(Counter.next(), parsePropertyAccess(qualified.getQualifier), qualified.getName.getText, location);
      }
    }
  }

  def parseStmList(stmList: PStmList): List[Statement] = {
    stmList match {
      case one: AOneStmList => parseStm(one.getStm) :: Nil;
      case many: AManyStmList => parseStmList(many.getList) :+ parseStm(many.getStm);
    }
  }

  def parseStm(stm: PStm): Statement = {
    stm match {
      case outStm: AOutStm => {
        val location = SourceLocation(fileName, outStm.getOut.getLine, outStm.getOut.getPos);
        OutStatement(Counter.next(), parseExp(outStm.getExp), location);
      }
      case assignStm: AAssignStm => {
        val location = SourceLocation(fileName, assignStm.getAssign.getLine, assignStm.getAssign.getPos);
        AssignStatement(Counter.next(), parsePropertyAccess(assignStm.getPropertyAccess), parseExp(assignStm.getExp), location);
      }
      case invokeStm: AInvokeStm => {
        val location = SourceLocation(fileName, invokeStm.getLParen.getLine, invokeStm.getLParen.getPos)
        CallStatement(Counter.next(), parseExp(invokeStm.getExp), location)
      }
      case ifStm: AIfStm => {
        val location = SourceLocation(fileName, ifStm.getIf.getLine, ifStm.getIf.getPos);
        IfStatement(Counter.next(), parseExp(ifStm.getCond), parseStmList(ifStm.getBody), location);
      }
      case whileStm: AWhileStm => {
        val location = SourceLocation(fileName, whileStm.getWhile.getLine, whileStm.getWhile.getPos);
        WhileStatement(Counter.next(), parseExp(whileStm.getCond), parseStmList(whileStm.getBody), location);
      }
    }
  }

}